import Taro from '@tarojs/taro';
import type { Response } from '../types/http';

// 内存缓存
const requestCache = new Map<string, { data: any; timestamp: number }>();
const CACHE_EXPIRATION_MS = 5 * 60 * 1000; // 缓存5分钟

// 请求方法类型
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'TRACE' | 'CONNECT';

// 自定义请求选项
interface CustomRequestOptions {
  showLoading?: boolean;
  showError?: boolean;
  loadingText?: string;
  cache?: boolean;
  retry?: number;
}

// 默认配置
const DEFAULT_OPTIONS: CustomRequestOptions = {
  showLoading: true,
  showError: true,
  loadingText: '加载中...',
  cache: false,
  retry: 0,
};

// 获取完整请求地址
const getBaseUrl = () => process.env.VITE_APP_BASE_API || '';

// 处理请求错误
const handleError = (error: any, showError = true) => {
  const message = error.message || '请求失败';
  if (showError) {
    Taro.showToast({
      title: message,
      icon: 'none',
      duration: 2000,
    });
  }
  return Promise.reject(error);
};

// 显示加载提示
const displayLoading = (loadingText: string = '加载中...') => {
  Taro.showLoading({ title: loadingText, mask: true });
};

// 隐藏加载提示
const hideLoading = () => {
  Taro.hideLoading();
};

/**
 * 生成缓存键
 * @param url 请求URL
 * @param data 请求数据
 */
const generateCacheKey = (url: string, data?: any): string => {
  return `${url}?${JSON.stringify(data || {})}`;
};

/**
 * 清除过期缓存
 */
const cleanExpiredCache = (): void => {
  const now = Date.now();
  for (const [key, value] of requestCache.entries()) {
    if (now - value.timestamp > CACHE_EXPIRATION_MS) {
      requestCache.delete(key);
    }
  }
};

// 定期清理过期缓存
setInterval(cleanExpiredCache, CACHE_EXPIRATION_MS);

/**
 * 统一请求方法
 * @param url 请求地址
 * @param method 请求方法
 * @param data 请求数据
 * @param options 自定义选项
 */
const request = async function<T = any>(
  url: string,
  method: HttpMethod = 'GET',
  data?: any,
  options: CustomRequestOptions = {}
): Promise<T> {
  // 合并选项
  const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
  const { showLoading, showError, loadingText, cache, retry } = mergedOptions;

  // 处理 GET 请求缓存
  if (method === 'GET' && cache) {
    const cacheKey = generateCacheKey(url, data);
    const cachedItem = requestCache.get(cacheKey);
    if (cachedItem && Date.now() - cachedItem.timestamp < CACHE_EXPIRATION_MS) {
      return Promise.resolve(cachedItem.data as T);
    }
  }

  // 显示加载提示
  if (showLoading) {
    displayLoading(loadingText);
  }

  // 添加token到请求头
  const token = Taro.getStorageSync('token');
  const authHeader = token ? { 'Authorization': `Bearer ${token}` } : {};

  let retryCount = 0;
  
  // 重试逻辑
  const executeRequest = async (): Promise<T> => {
    try {
      // 构建请求参数
      const requestOptions: Taro.request.Option = {
        url: /^(http|https):\/\//.test(url) ? url : getBaseUrl() + url,
        method,
        data,
        header: {
          'Content-Type': 'application/json',
          ...authHeader,
        },
      };
      
      const res = await Taro.request<Response<T>>(requestOptions);

      // 隐藏加载提示
      if (showLoading) {
        hideLoading();
      }

      // 处理响应
      const { statusCode, data: responseData } = res;
      if (statusCode >= 200 && statusCode < 300) {
        if (responseData.code === 0) {
          // 对成功的 GET 请求进行缓存
          if (method === 'GET' && cache) {
            const cacheKey = generateCacheKey(url, data);
            requestCache.set(cacheKey, { 
              data: responseData.data, 
              timestamp: Date.now() 
            });
          }
          return responseData.data;
        }
        
        throw new Error(responseData.message || '请求失败');
      }
      
      throw new Error(`HTTP错误: ${statusCode}`);
    } catch (error) {
      // 重试逻辑
      if (retryCount < (retry || 0)) {
        retryCount++;
        return executeRequest();
      }
      
      if (showLoading) {
        hideLoading();
      }
      throw error;
    }
  };

  try {
    return await executeRequest();
  } catch (error) {
    return handleError(error, showError);
  }
};

// 导出请求方法
export default {
  get: <T = any>(url: string, data?: any, options?: CustomRequestOptions) => 
    request<T>(url, 'GET', data, options),

  post: <T = any>(url: string, data?: any, options?: CustomRequestOptions) => 
    request<T>(url, 'POST', data, options),

  put: <T = any>(url: string, data?: any, options?: CustomRequestOptions) => 
    request<T>(url, 'PUT', data, options),

  delete: <T = any>(url: string, data?: any, options?: CustomRequestOptions) => 
    request<T>(url, 'DELETE', data, options),
};